package com.syh.jn.scm.domain.supplier.entity.finance;

import cn.redscarf.commons.spring.SpringContextUtils;
import cn.redscarf.commons.wrapper.Restrictions;
import cn.redscarf.commons.wrapper.Wrapper;
import cn.redscarf.commons.wrapper.expression.orderExpression.Order;
import com.syh.core.ddd.domain.AbstractSimpleAggregate;
import com.syh.jn.scm.domain.operation.entity.supplier.OperaSupplier;
import com.syh.jn.scm.domain.operation.repository.supplier.OperaSupplierReadRepository;
import com.syh.jn.scm.domain.supplier.entity.logistics.LogisticsBill;
import com.syh.jn.scm.domain.supplier.entity.logistics.LogisticsBillItem;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseOrder;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseOrderItem;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseReturnOrder;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseReturnOrderItem;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleOrder;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleOrderItem;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleReturnOrder;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleReturnOrderItem;
import com.syh.jn.scm.domain.supplier.repository.finance.BusinessAccountRecordReadRepository;
import com.syh.jn.scm.domain.supplier.repository.finance.archive.ArchiveReadRepository;
import com.syh.jn.scm.domain.supplier.repository.purchase.PurchaseOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.purchase.PurchaseOrderRepository;
import com.syh.jn.scm.domain.supplier.repository.purchase.PurchaseReturnOrderRepository;
import com.syh.jn.scm.domain.supplier.repository.sale.SaleOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.sale.SaleOrderRepository;
import com.syh.jn.scm.domain.supplier.repository.sale.SaleReturnOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.sale.SaleReturnOrderRepository;
import com.syh.jn.scm.exception.BusinessException;
import com.syh.jn.scm.service.api.supplier.finance.enums.BusinessAccountRecordEnums;
import com.syh.jn.scm.service.api.supplier.finance.enums.OrderExpensesBillEnums;
import com.syh.jn.scm.service.api.supplier.logistics.enums.LogisticsBillEnums;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 单位应收应付记录
 */
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class BusinessAccountRecord extends AbstractSimpleAggregate<Long> {

  //服务商费用单
  public BusinessAccountRecord(CostBill costBill, Short dataType) {
    this.businessEntityId = costBill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(costBill.getInputTimestamp());
    this.recordTimeStamp = costBill.getInputTimestamp();
    super.initDefault();

    this.originalDocumentId = costBill.getId();
    this.originalDocumentCode = costBill.getCode();
    this.originalDocumentType = BusinessAccountRecordEnums.Type.COST_BILL.getStatus();

    //默认客户类型为上游供应商和物流  费用单为增加应付金额
    if(dataType.intValue() == 1){
      this.changeAmount = costBill.getPrice();
    }else{
      this.changeAmount = costBill.getPrice().negate();
    }
    //如果客户类型为下游客户 则把值加负号 为减少应收金额
    if (costBill.getCustomerType() == 1) {
      this.changeAmount = this.changeAmount.negate();
    }
    this.changeAmount = this.changeAmount.setScale(2,BigDecimal.ROUND_HALF_UP);
    this.dataType = dataType;
    this.customerId = costBill.getCustomerId();
    this.customerType = Short.valueOf(costBill.getCustomerType().toString());

  }

  //服务商收入单
  public BusinessAccountRecord(IncomeBill incomeBill, Short dataType) {
    this.businessEntityId = incomeBill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(incomeBill.getInputTimestamp());
    this.recordTimeStamp = incomeBill.getInputTimestamp();
    super.initDefault();

    this.originalDocumentId = incomeBill.getId();
    this.originalDocumentCode = incomeBill.getCode();
    this.originalDocumentType = BusinessAccountRecordEnums.Type.INCOME_BILL.getStatus();

    //默认客户类型为上游供应商和物流  收入单为减少应付金额
    if (dataType.intValue() == 1){
      this.changeAmount = incomeBill.getPrice().negate();
    }else{
      this.changeAmount = incomeBill.getPrice();
    }
    //如果客户类型为下游客户 则把值加负号 为增加应收金额
    if (incomeBill.getCustomerType() == 1) {
      this.changeAmount = this.changeAmount.negate();
    }

    this.changeAmount = this.changeAmount.setScale(2,BigDecimal.ROUND_HALF_UP);
    this.dataType = dataType;
    this.customerId = incomeBill.getCustomerId();
    this.customerType = incomeBill.getCustomerType();
  }

  //服务商物流单据
  public BusinessAccountRecord(LogisticsBill bill, Short dataType) {
    this.businessEntityId = bill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    //根据发货状态获取记录时间
    switch (bill.getLogisticsState()) {
      //发货
      case 1:
        this.checkArchiveState(bill.getDeliveryTimestamp());
        this.recordTimeStamp = bill.getDeliveryTimestamp();
        break;
      //签收
      case 2:
        this.checkArchiveState(bill.getStorageTimestamp());
        this.recordTimeStamp = bill.getStorageTimestamp();
        break;
    }
    Short recordType;
    //计算物流订单的商品总额
    BigDecimal changeAmount = BigDecimal.ZERO;
    switch (bill.getOrderType()) {
      case 0://销售订单
        recordType = BusinessAccountRecordEnums.Type.SALE_ORDER.getStatus();
        SaleOrderReadRepository saleOrderReadRepository = (SaleOrderReadRepository) SpringContextUtils.getBean("saleOrderRepositoryImpl");
        SaleOrder saleOrder = saleOrderReadRepository.getById(bill.getOrderId());
        for (LogisticsBillItem logisticsBillItem: bill.getItems()) {
          for (SaleOrderItem saleOrderItem: saleOrder.getItems()) {
            if (logisticsBillItem.getOrderItemId().equals(saleOrderItem.getId())) {
              changeAmount = changeAmount.add(saleOrderItem.getPrice().multiply(logisticsBillItem.getQuantity()));
              break;
            }
          }
        }
        break;
      case 1://销售退货单
        recordType = BusinessAccountRecordEnums.Type.SALE_RETURN_ORDER.getStatus();
        SaleReturnOrderReadRepository saleReturnOrderReadRepository = (SaleReturnOrderReadRepository) SpringContextUtils.getBean(SaleReturnOrderReadRepository.class);
        SaleReturnOrder saleReturnOrder = saleReturnOrderReadRepository.getById(bill.getOrderId());
        for (LogisticsBillItem logisticsBillItem: bill.getItems()) {
          for (SaleReturnOrderItem saleReturnOrderItem: saleReturnOrder.getItems()) {
            if (logisticsBillItem.getOrderItemId().equals(saleReturnOrderItem.getId())) {
              changeAmount = changeAmount.subtract(saleReturnOrderItem.getPrice().multiply(logisticsBillItem.getQuantity()));
              break;
            }
          }
        }
        break;
      case 2://采购订单
        recordType = BusinessAccountRecordEnums.Type.PURCHASE_ORDER.getStatus();
        PurchaseOrderReadRepository purchaseOrderReadRepository = (PurchaseOrderReadRepository) SpringContextUtils.getBean("purchaseOrderRepositoryImpl");
        PurchaseOrder purchaseOrder = purchaseOrderReadRepository.getById(bill.getOrderId());
        for (LogisticsBillItem logisticsBillItem: bill.getItems()) {
          for (PurchaseOrderItem purchaseOrderItem: purchaseOrder.getItems()) {
            if (logisticsBillItem.getOrderItemId().equals(purchaseOrderItem.getId())) {
              changeAmount = changeAmount.add(purchaseOrderItem.getPrice().multiply(logisticsBillItem.getQuantity()));
              break;
            }
          }
        }
        break;
      case 3://采购退货订单
        recordType = BusinessAccountRecordEnums.Type.PURCHASE_RETURN_ORDER.getStatus();
        PurchaseReturnOrderRepository purchaseReturnOrderRepository = (PurchaseReturnOrderRepository) SpringContextUtils.getBean(PurchaseReturnOrderRepository.class);
        PurchaseReturnOrder purchaseReturnOrder = purchaseReturnOrderRepository.getById(bill.getOrderId());
        for (LogisticsBillItem logisticsBillItem: bill.getItems()) {
          for (PurchaseReturnOrderItem purchaseReturnOrderItem: purchaseReturnOrder.getItems()) {
            if (logisticsBillItem.getOrderItemId().equals(purchaseReturnOrderItem.getId())) {
              changeAmount = changeAmount.subtract(purchaseReturnOrderItem.getPrice().multiply(logisticsBillItem.getQuantity()));
              break;
            }
          }
        }
        break;
      default:
        throw new BusinessException("订单类型错误！");
    }

    //预收买家的物流订单费用纳入应收付记录
    if(bill.getFreightTake() != null && bill.getFreightTake().equals(LogisticsBillEnums.FreightTake.BUY.getStatus())
            && bill.getFreightType().equals(LogisticsBillEnums.FreightType.BEFORE.getStatus())){
      switch (recordType){
        case 1://销售订单、采购订单的物流费用为正数相加
        case 5:
          changeAmount = changeAmount.add(bill.getTotalFreight());
          break;
        case 2:
        case 6://销售退货订单、采购退货订单的物流费用为负数相加
          changeAmount = changeAmount.add(bill.getTotalFreight().negate());
          break;
      }
    }

    if(dataType.intValue() == 1){
      this.changeAmount = changeAmount;
    }else{
      this.changeAmount = changeAmount.negate();
    }
    this.changeAmount = this.changeAmount.setScale(2,BigDecimal.ROUND_HALF_UP);
    this.dataType = dataType;
    this.customerId = bill.getCustomerId();
    this.customerType = bill.getCustomerType();
    this.originalDocumentId = bill.getOrderId();
    this.originalDocumentCode = bill.getOrderCode();
    this.originalLogisticsId = bill.getId();
    this.originalLogisticsCode = bill.getCode();
    this.originalDocumentType = recordType;
    super.initDefault();
  }

  //服务商付款单
  public BusinessAccountRecord(PaymentBill paymentBill, Short dataType) {
    this.businessEntityId = paymentBill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(paymentBill.getInputTimestamp());
    this.recordTimeStamp = paymentBill.getInputTimestamp();
    super.initDefault();

    this.originalDocumentId = paymentBill.getId();
    this.originalDocumentCode = paymentBill.getCode();
    this.originalDocumentType = BusinessAccountRecordEnums.Type.PAYMENT_BILL.getStatus();
    if (dataType.intValue() == 1) {
      this.rePayAmount = paymentBill.getPrice();
      this.changeAmount = paymentBill.getPrice().negate();
    } else {
      this.rePayAmount = paymentBill.getPrice().negate();
      this.changeAmount = paymentBill.getPrice();
    }
    this.rePayAmount = this.rePayAmount.setScale(2,BigDecimal.ROUND_HALF_UP);
    this.changeAmount = this.changeAmount.setScale(2,BigDecimal.ROUND_HALF_UP);
    this.dataType = dataType;
    this.customerId = paymentBill.getCustomerId();
    this.customerType = paymentBill.getCustomerType();
  }

  //服务商收款单
  public BusinessAccountRecord(ReceiptBill bill, Short dataType) {
    this.businessEntityId = bill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(bill.getInputTimestamp());
    this.recordTimeStamp = bill.getInputTimestamp();
    super.initDefault();

    this.originalDocumentId = bill.getId();
    this.originalDocumentCode = bill.getCode();
    this.originalDocumentType = BusinessAccountRecordEnums.Type.RECEIPT_BILL.getStatus();
    if (dataType.intValue() == 1) {
      this.rePayAmount = bill.getPrice();
      this.changeAmount = bill.getPrice().negate();
    } else {
      this.rePayAmount = bill.getPrice().negate();
      this.changeAmount = bill.getPrice();
    }
    this.rePayAmount = this.rePayAmount.setScale(2,BigDecimal.ROUND_HALF_UP);
    this.changeAmount = this.changeAmount.setScale(2,BigDecimal.ROUND_HALF_UP);
    this.dataType = dataType;
    this.customerId = bill.getCustomerId();
    this.customerType = bill.getCustomerType();
  }

  //物流公司物流单 物流公司id字段仅用于与区别其他构造函数
  public BusinessAccountRecord(LogisticsBill bill, Short dataType,Long logisticsId) {
    this.businessEntityId = bill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    //物流公司应收付只在签收时生成  所以只需获取签收时间
    this.checkArchiveState(bill.getStorageTimestamp());
    this.recordTimeStamp = bill.getStorageTimestamp();
    super.initDefault();
    this.originalDocumentId = bill.getId();
    this.originalDocumentCode = bill.getCode();
    if (bill.getOrderType().equals(LogisticsBillEnums.OrderType.SALE.getStatus())) {
      this.originalDocumentType = BusinessAccountRecordEnums.Type.SALE_LOGISTICS_BILL.getStatus();
    }
    else {
      this.originalDocumentType = BusinessAccountRecordEnums.Type.PURCHASE_RETURN_LOGISTICS_BILL.getStatus();
    }
    if (dataType.intValue() == 1) {
      this.rePayAmount = bill.getTotalFreight();
      this.changeAmount = bill.getTotalFreight();
    } else {
      this.rePayAmount = bill.getTotalFreight().negate();
      this.changeAmount = bill.getTotalFreight().negate();
    }
    this.rePayAmount = this.rePayAmount.setScale(2,BigDecimal.ROUND_HALF_UP);
    this.changeAmount = this.changeAmount.setScale(2,BigDecimal.ROUND_HALF_UP);
    this.dataType = dataType;
    this.customerId = bill.getLogisticsCompanyId();
    this.customerType = Short.valueOf("2");
  }

  //订单费用应收付记录
  public BusinessAccountRecord(OrderExpensesBill orderExpensesBill, Short dataType) {
    this.businessEntityId = orderExpensesBill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(orderExpensesBill.getConfirmTimeStamp());
    this.recordTimeStamp = orderExpensesBill.getConfirmTimeStamp();
    super.initDefault();
    this.originalDocumentId = orderExpensesBill.getSourceId();
    this.originalDocumentCode = orderExpensesBill.getSourceCode();
    switch (orderExpensesBill.getOrderType()) {
      //销售单
      case 1:
        SaleOrderRepository saleOrderReadRepository = (SaleOrderRepository) SpringContextUtils.getBean("saleOrderRepositoryImpl");
        SaleOrder saleOrder = saleOrderReadRepository.getById(orderExpensesBill.getSourceId());
        this.customerId = saleOrder.getDownStreamCompanyId();
        this.customerType = 1;
        this.originalDocumentType = BusinessAccountRecordEnums.Type.SALE_OTHER_EXPENSES.getStatus();
        break;
      //采购单
      case 2:
        PurchaseOrderRepository purchaseOrderReadRepository = (PurchaseOrderRepository) SpringContextUtils.getBean("purchaseOrderRepositoryImpl");
        PurchaseOrder purchaseOrder = purchaseOrderReadRepository.getById(orderExpensesBill.getSourceId());
        this.customerId = purchaseOrder.getUpStreamCompanyId();
        this.customerType = 0;
        this.originalDocumentType = BusinessAccountRecordEnums.Type.PURCHASE_OTHER_EXPENSES.getStatus();
        break;
    }
    //计算需要纳入总额的费用生成应收付金额
    this.changeAmount = BigDecimal.ZERO;
    orderExpensesBill.getItems().forEach(orderExpensesBillItem -> {
      if (orderExpensesBillItem.getIncludeTotalAmount().equals(OrderExpensesBillEnums.IncludeTotalAmount.INCLUDE.getStatus())) {
        this.changeAmount = this.changeAmount.add(orderExpensesBillItem.getExpenses());
      }
    });
    this.dataType = dataType;
    if (dataType.intValue() != 1) {
      this.changeAmount = this.changeAmount.negate();
    }


  }

  //生成订单的优惠金额应收付记录
  public void discount(LogisticsBill bill, Short dataType) {
    this.businessEntityId = bill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.recordTimeStamp = this.getDiscountRecordTimeStamp(bill,dataType);
    this.checkArchiveState(this.recordTimeStamp);
    BigDecimal changeAmount = BigDecimal.ZERO;
    switch (bill.getOrderType()) {
      case 0://销售订单
        SaleOrderRepository saleOrderReadRepository = (SaleOrderRepository) SpringContextUtils.getBean("saleOrderRepositoryImpl");
        SaleOrder sale = saleOrderReadRepository.getById(bill.getOrderId());
        changeAmount = changeAmount.add(sale.getDiscount()).negate();
        this.originalDocumentType = BusinessAccountRecordEnums.Type.SALE_DISCOUNT.getStatus();
        break;
      case 1://销售退货单
        SaleReturnOrderRepository saleReturnOrderReadRepository = (SaleReturnOrderRepository) SpringContextUtils.getBean(SaleReturnOrderRepository.class);
        SaleReturnOrder saleReturnOrder = saleReturnOrderReadRepository.getById(bill.getOrderId());
        changeAmount = changeAmount.add(saleReturnOrder.getDiscount());
        this.originalDocumentType = BusinessAccountRecordEnums.Type.SALE_RETURN_DISCOUNT.getStatus();
        break;
      case 2://采购订单
        PurchaseOrderRepository purchaseOrderReadRepository = (PurchaseOrderRepository) SpringContextUtils.getBean("purchaseOrderRepositoryImpl");
        PurchaseOrder purchaseOrder = purchaseOrderReadRepository.getById(bill.getOrderId());
        changeAmount = changeAmount.add(purchaseOrder.getDiscount()).negate();
        this.originalDocumentType = BusinessAccountRecordEnums.Type.PUR_DISCOUNT.getStatus();
        break;
      case 3://采购退货订单
        PurchaseReturnOrderRepository purchaseReturnOrderRepository = (PurchaseReturnOrderRepository) SpringContextUtils.getBean(PurchaseReturnOrderRepository.class);
        PurchaseReturnOrder purchaseReturnOrder = purchaseReturnOrderRepository.getById(bill.getOrderId());
        changeAmount = changeAmount.add(purchaseReturnOrder.getDiscount());
        this.originalDocumentType = BusinessAccountRecordEnums.Type.PUR_RETURN_DISCOUNT.getStatus();
        break;
      default:
        throw new BusinessException("订单类型错误！");
    }

    if(dataType.intValue() == 1){
      this.changeAmount = changeAmount;
    }else{
      this.changeAmount = changeAmount.negate();
    }
    this.changeAmount = this.changeAmount.setScale(2,BigDecimal.ROUND_HALF_UP);
    this.dataType = dataType;
    this.customerId = bill.getCustomerId();
    this.customerType = bill.getCustomerType();
    this.originalDocumentId = bill.getOrderId();
    this.originalDocumentCode = bill.getOrderCode();
//    this.originalLogisticsId = bill.getId();
//    this.originalLogisticsCode = bill.getCode();

    super.initDefault();
  }

  /**
   * 获取优惠金额应收付记录的业务记录时间
   * @param bill
   * @param dataType
   * @return
   */
  private Long getDiscountRecordTimeStamp(LogisticsBill bill, Short dataType) {
    Long recordTimeStamp = null;
    //正常操作取物流单的发货时间为记录时间
    if (dataType.intValue() == 1) {
      recordTimeStamp = bill.getDeliveryTimestamp();
    } else { //回滚操作拿对应正常操作的记录时间
      Short originalDocumentType = null;
      //获取优惠类型
      switch (bill.getOrderType()) {
        case 0://销售单
          originalDocumentType = BusinessAccountRecordEnums.Type.SALE_DISCOUNT.getStatus();
          break;
        case 1://销售退货单
          originalDocumentType = BusinessAccountRecordEnums.Type.SALE_RETURN_DISCOUNT.getStatus();
          break;
        case 2://采购单
          originalDocumentType = BusinessAccountRecordEnums.Type.PUR_DISCOUNT.getStatus();
          break;
        case 3://采购退货单
          originalDocumentType = BusinessAccountRecordEnums.Type.PUR_RETURN_DISCOUNT.getStatus();
          break;
      }
      BusinessAccountRecordReadRepository businessAccountRecordReadRepository = (BusinessAccountRecordReadRepository) SpringContextUtils.getBean("businessAccountRecordRepositoryImpl");
      Wrapper wrapper = Wrapper.build()
              .append(Restrictions.eq("originalDocumentId",bill.getOrderId()))
              .append(Restrictions.eq("originalDocumentType",originalDocumentType))
              .order(Order.desc("createTimeStamp"));
      BusinessAccountRecord businessAccountRecord = businessAccountRecordReadRepository.getOne(wrapper);
      recordTimeStamp = businessAccountRecord.getRecordTimeStamp();
    }
    return recordTimeStamp;
  }

  /**
   * 检查服务商是否开账
   */
  private void checkSupplierOpenAccountState() {
    OperaSupplierReadRepository operaSupplierReadRepository = (OperaSupplierReadRepository) SpringContextUtils.getBean("operaSupplierRepositoryImpl");
    OperaSupplier operaSupplier = operaSupplierReadRepository.getById(this.businessEntityId);
    if (!Short.valueOf("1").equals(operaSupplier.getOpenAccountState())) {
      throw new BusinessException("当前服务商未开账，生成应收付记录失败！");
    }
  }

  /**
   * 根据提供的时间戳判断是否在结存范围内
   * @param timeStamp
   * @return
   */
  private void checkArchiveState(Long timeStamp) {
    ArchiveReadRepository archiveReadRepository = (ArchiveReadRepository) SpringContextUtils.getBean("archiveRepoImpl");
    Wrapper wrapper = Wrapper.build().append(Restrictions.gt("archiveEndTimeStamp",timeStamp));
    if (archiveReadRepository.getCount(wrapper) > 0) {
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

      throw new BusinessException("该应收付时间"+sdf.format(new Date(timeStamp))+"已结存，创建失败！");
    }
  }


  /**
   * 原始订单单据ID
   */
  private Long originalDocumentId;
  /**
   * 原始订单单据编号
   */
  private String originalDocumentCode;
  /**
   * 原始物流单据ID
   */
  private Long originalLogisticsId;
  /**
   * 原始物流单据编号
   */
  private String originalLogisticsCode;
  /**
   * 原始单据类型: 正的有(1销售单,2采购退货单,3收入单,4付款单),负的有(5采购单,6销售退货单,7费用单,8收款单)
   */
  private Short originalDocumentType;
  /**
   * 收付款金额
   */
  private BigDecimal rePayAmount;
  /**
   * 增减应收付金额(负的为-,正的为+)
   */
  private BigDecimal changeAmount;
  /**
   * 汇总余额
   */
  private BigDecimal summaryBalance;
  /**
   * 客户id
   */
  private Long customerId;
  /**
   * 客户类型: 0上游, 1下游, 2物流
   */
  private Short customerType;
  /**
   * 数据类型: 1正规操作, 2回滚操作
   */
  private Short dataType;
  /**
   * 归属端id
   */
  private Long businessEntityId;
  /**
   * 业务记录日期
   */
  private Long recordTimeStamp;
}
